<?php
/**
 * Created by PhpStorm.
 * User: shutao
 * Date: 2018/9/14
 * Time: 10:44
 */

namespace wechat\controllers;

use tt\common\help\HookHelp;
use tt\common\help\LogHelp;
use tt\common\help\ResponseHelp;
use ttcommon\aop\annotation\ControllerAnnotation;
use ttext\backend\admin\models\User;
use ttext\backend\admin\services\UserService;
use ttext\backend\admin\services\UserTokenService;
use wechat\hook\AuthHook;
use wechat\hook\WechatOn;
use wechat\models\WeUser;
use wechat\models\WeUserOpenid;
use wechat\services\WeChatMessage;
use wechat\services\WeConfigService;
use wechat\services\WeiXinService;
use yii\base\UserException;

/*
 * 流程简介
 * 1. 用户关注了公众号
 * 用户关注时，此时没有 unionid，因此我们使用用户的 openid 作为临时的标识
 * 1.1 在 tt_user 中为用户注册了一个账号(userid 为 openid)
 * 1.2 在 we_user 中登记，关联 tt_user
 * 1.3 在 we_user_openid 中登记 we_user，并标明用户的来源（注意 we_user_openid.openid 为公众号的 openid，而不是用户的 openid)
 * 详见：wechat/hook/AuthHook:wechatEvent
 *
 * 2. 用户授权
 * ServerController:actionTest，如果授权成功，那么可以获取到用户的 openid, unionid；
 * 此时需要 2.1 使用 unionid 来替换掉第1步中的 tt_user.uuid, we_user.unionid
 * 此时不需要在 we_user_openid 中添加记录
 */

/**
 * @title [微信]平台连接
 * @access public
 */
class ServerController extends BaseWechatController implements ControllerAnnotation
{

    public $enableCsrfValidation = false;

    // ------------------- 警告 -----------------------
    // 这个接口只是用来接收微信服务器的信息，原则上不允许其它用途的访问
    /**
     * @title URL
     * @link ?r=wechat/server/index
     */
    public function actionIndex($appid = '')
    {
//        pr($_SESSION);
        // 在公共号平台：必须看到"服务器配置（已启用）" 才是真正的启用
        $config = $appid ? WeiXinService::weConfigFromAppid($appid) : WeiXinService::defaultWeConfig();
        $app = WeiXinService::app($config);

        // 接收/回复用户的信息
        !isset($_GET['echostr']) &&
        $app->server->push(function ($message) use ($config) {
            // 消息的类型 https://www.easywechat.com/docs/master/zh-CN/official-account/server
            /** @var WeChatMessage $wMessage */
            $wMessage = new WeChatMessage($message);
            $wMessage->config = $config;
            // 如果有自定义的处理机制，并且有返回信息
//            LogHelp::message('wechat_' . $wMessage->msgType,'wechat');

            if ($responseData = HookHelp::on('wechat_' . $wMessage->msgType, $wMessage)) {
//                LogHelp::message($responseData,'response');
                return $responseData;
            }
            // 微信的默认回复信息
            return 'success';
        });
        // $app->server->serve() 就是执行服务端业务了，那么它的返回值是一个 Symfony\Component\HttpFoundation\Response 实例。
        $response = $app->server->serve();
        // 我这里是直接调用了它的 send() 方法，它就是直接输出（echo）了，我们在一些框架就不能直接输出了，那你就直接拿到 Response 实例后做相应的操作即可
        $response->send();
        exit;
    }

    /**
     * @title 授权测试
     * @param string $appid
     * @throws \Exception
     */
    public function actionTest($appid)
    {
        /*
         * 可能出现的坑
         * 1. redirect_uri 域名与后台配置不一致，错误码：10003
         * 解决：使用 LogHelp 记录下回调地址，并你爱我 urldecode 将其解码：比较 appid 和 redirect_uri 是否正确匹配（之前出现过 redirect_uri 正确，但 appid 是错误的）
         */
        LogHelp::message('授权测试:appid:' . $appid);
        WeiXinService::wechatOauth($appid);
        HookHelp::on(WechatOn::FINISH_WECHAT_AUTH);
        ResponseHelp::redirect('/index.php'); // TODO 如果都没有处理，都跳转到主页
        echo 'welcome:你已经授权登录了', PHP_EOL;
        echo '<pre>';
        print_r($_SESSION);
        exit;
    }

    // ----------------- 警告：不要直接访问此操作；这是授权成功后的响应地址 --------------

    /**
     * @title 授权回调
     * 需要将网站域名添加到微信的授权配置中
     */
    public function actionCallback($appid = '')
    {
        $config = $appid ? WeiXinService::weConfigFromAppid($appid) : WeiXinService::defaultWeConfig();
        $app = WeiXinService::app($config);
        $oauth = $app->oauth;

        // 获取 OAuth 授权结果用户信息
        // https://github.com/overtrue/socialite#user-interface
        // https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
        $user = $oauth->user()->toArray(); // 微信原样的信息

        /*
Array
(
    [id] => 28位的字符串
    [name] => 名称
    [nickname] => 名称
    [avatar] => 至少 133 位的字符串
    [email] =>
    [original] => Array // $user->getOriginal();
        (
            [openid] => 用户的唯一标识（28位的字符串）（同上面的 id)
            [nickname] => 昵称
            [sex] => 1 值为1时是男性，值为2时是女性，值为0时是未知
            [language] => zh_CN
            [city] => 深圳
            [province] => 广东
            [country] => 中国
            [headimgurl] => 至少 133 位的字符串，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像）
        // 头像地址示例 http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLojKOZ5zicspOiaRpaWSdGQtPAaZL0PiaibJL6JZkPLAWJPDsygjhItaiaqLfZ31z8icB6Olqgj9Vnokjw/132
            [privilege] => Array() 用户特权信息，json 数组，如微信沃卡用户为（chinaunicom）
            [unionid] => 28 位的字符串 只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。
        )

    [token] => Overtrue\Socialite\AccessToken Object
        (
            [attributes:protected] => Array
                (
                    [access_token] => 89 位的字符串
                    [expires_in] => 7200
                    [refresh_token] => 89 位的字符串
                    [openid] => 最上面的 id 28 位的字符串
                    [scope] => snsapi_userinfo
                    [unionid] => original.unionid 中 28 位的字符串
                )

        )

    [provider] => WeChat
)
         */
        // 登记微信用户的信息
        // 如果返回的是 finished 字符串，则会拦截注册等事件
        if ('finished' == HookHelp::on(WechatOn::SNSAPI_USERINOF_SUCCESS, $user)) {
            return;
        }
        $original = $user['original'];
        // 在用户关注了微信公众号时，我们就使用了用户的 openid 为用户进行了注册
        if ($userTmp = WeUserOpenid::find()->where(['user_openid' => $original['openid']])
            ->select(['we_id'])->asArray()->one()) {
            $weUser = WeUser::findOne($userTmp['we_id']);
            if ($weUser->weixin_unionid != $original['unionid']) { // 还没有替换
                $weUser->nickname = $original['nickname'];
                $weUser->sex = (int)$original['sex'];
                $weUser->city = $original['city'];
                $weUser->province = $original['province'];
                $weUser->country = $original['country'];
                $weUser->avatar = $original['headimgurl'];
                $weUser->weixin_unionid = $original['unionid'];
                $weUser->save();
                // 替换 user
                if (AuthHook::$createUserWhenFirstSubscribe) {
                    User::updateAll(['userid' => $original['unionid']], ['id' => $weUser->uid]);
                }
            }
        } else {
            // 如果不存在（可能是先授权，没关注），则需要重新注册
            $transaction = \Yii::$app->db->beginTransaction();
            try {
                $uid = AuthHook::$createUserWhenFirstSubscribe
                    ? $user = UserService::registerWithUuid($original['unionid'])
                    : 0;

                $weUser = new WeUser();
                $weUser->nickname = $original['nickname'];
                $weUser->sex = (int)$original['sex'];
                $weUser->city = $original['city'];
                $weUser->province = $original['province'];
                $weUser->country = $original['country'];
                $weUser->avatar = $original['headimgurl'];
                $weUser->weixin_unionid = $original['unionid'];
                $weUser->uid = $uid;
                $weUser->insert();

                $weUserOpenid = new WeUserOpenid();
                $weUserOpenid->we_id = $weUser->id;
                $weUserOpenid->openid = $config['appid'];
                $weUserOpenid->user_openid = $original['openid'];
                $weUserOpenid->type = WeConfigService::typeFromAppid($config['appid']);
                $weUserOpenid->insert();
                $transaction->commit();
            } catch (\Exception $e) {
                $transaction->rollBack();
                LogHelp::message($e->getMessage(), 'wechatOauth');
                throw new UserException('授权失败');
            }
        }

        $userToken = UserTokenService::create($weUser->uid)->toArray();
        // 尽量与 ttext/backend/admin/hook/AuthHook::authSign 中网页注册保存一致（非必须，只要当前类的 AuthHook::userId 中能够正确解析就可以了
        UserTokenService::tokenToLoginSign($userToken);
        HookHelp::on(WechatOn::FINISH_WECHAT_AUTH);

        // 如果有跳转地址，则跳转
        if (\Yii::$app->session->has('target_url')) {
            $url = \Yii::$app->session->get('target_url');
            \Yii::$app->session->remove('target_url');
            if ($url) {
                ResponseHelp::redirect($url);
            }
        }
        echo 'TODO: 授权成功，监听 WechatOn::FINISH_WECHAT_AUTH 可处理后续操作';
        exit;
    }
}